package com.redepronik.negosys.invfac.service;

import static com.redepronik.negosys.utils.Utils.fechaFormatoString;
import static com.redepronik.negosys.utils.Utils.newBigDecimal;
import static com.redepronik.negosys.utils.Utils.presentaMensaje;
import static com.redepronik.negosys.utils.Utils.timestamp;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.redepronik.negosys.invfac.dao.IngresoDao;
import com.redepronik.negosys.invfac.entity.Bodega;
import com.redepronik.negosys.invfac.entity.Detalleingreso;
import com.redepronik.negosys.invfac.entity.Estadokardex;
import com.redepronik.negosys.invfac.entity.Ingreso;
import com.redepronik.negosys.invfac.entity.Kardex;
import com.redepronik.negosys.invfac.entity.Producto;
import com.redepronik.negosys.invfac.entity.Tipodocumento;
import com.redepronik.negosys.invfac.entityAux.ChequeReporte;

@Service
public class IngresoServiceImpl implements IngresoService {

	@Autowired
	private IngresoDao ingresoDao;

	@Autowired
	private ProductoService productoService;

	@Autowired
	private KardexService kardexService;

	@Autowired
	private BodegaService bodegaService;

	@Autowired
	private TipoDocumentoService tipoDocumentoService;

	public String actualizar(Ingreso ingreso) {
		ingresoDao.actualizar(ingreso);
		return "save";
	}

	public Long contar() {
		return (Long) ingresoDao.contar(Ingreso.class, "ingresoid");
	}

	public void comprobar(Ingreso ingreso, Tipodocumento tipoDocumento) {
		if (ingreso.getProveedor() == null)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"INGRESE UN PROVEEDOR", "error", false);
		else if (tipoDocumento.getTipodocumentoid() == 0)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE ESCOJER UN TIPO DE DOCUMENTO", "error", false);
		else if (ingreso.getCodigodocumento().equals(""))
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE INGRESAR UN NUMERO DE DOCUMENTO", "error", false);
		else if (ingreso.getFechafactura() == null)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE INGRESAR LA FECHA DE LA FACTURA", "error", false);
		else if (ingreso.getDetalleingresos() == null
				|| ingreso.getDetalleingresos().size() == 0)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE INGRESAR PRODUCTOS", "error", false);
		else
			presentaMensaje(FacesMessage.SEVERITY_INFO, "CORRECTO", "error",
					true);
	}

	public void eliminarDetalle(Ingreso ingreso, Detalleingreso detalleIngreso) {
		ingreso.removeDetalleingreso(detalleIngreso);
		presentaMensaje(FacesMessage.SEVERITY_INFO, "ELIMINÓ EL DETALLE: "
				+ detalleIngreso.getProducto().getEan());
	}

	public void eliminar(Ingreso ingreso) {
		if (ingreso.getActivo())
			ingreso.setActivo(false);
		else
			ingreso.setActivo(true);
		ingresoDao.actualizar(ingreso);
	}

	public boolean insertar(Ingreso ingreso) {
		boolean retorno = false;
		ingreso.setFechaingreso(timestamp());
		ingreso.setActivo(true);
		if (ingreso.getProveedor() == null)
			presentaMensaje(FacesMessage.SEVERITY_ERROR, "INGRESE UN PROVEEDOR");
		else if (ingreso.getTipodocumento().getTipodocumentoid() == 0)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE ESCOJER UN TIPO DE DOCUMENTO");
		else if (ingreso.getCodigodocumento().equals(""))
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE INGRESAR UN NUMERO DE DOCUMENTO");
		else if (ingreso.getFechafactura() == null)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE INGRESAR LA FECHA DE LA FACTURA");
		else if (ingreso.getTotal().compareTo(new BigDecimal("0")) == 0)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"INGRESE EL TOTAL DE LA FACTURA");
		else if (ingreso.getDetalleingresos() == null
				|| ingreso.getDetalleingresos().size() == 0)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"DEBE INGRESAR PRODUCTOS");
		else {
			ingresoDao.insertar(ingreso);
			retorno = true;

			List<Detalleingreso> detalleIngresos = ingreso.getDetalleingresos();
			for (Detalleingreso detalleIngreso : detalleIngresos) {
				Producto producto = productoService
						.obtenerPorProductoId(detalleIngreso.getProducto()
								.getProductoid());

				Bodega bodega = bodegaService.obtenerPorBodegaId(detalleIngreso
						.getBodega().getBodegaid());

				Kardex kardex = new Kardex();
				Estadokardex estadokardex = new Estadokardex();
				estadokardex.setEstadokardexid(4);

				kardex.setEstadokardex(estadokardex);
				kardex.setProducto(producto);
				kardex.setBodega(bodega);
				kardex.setNota(tipoDocumentoService.obtenerPorId(
						ingreso.getTipodocumento().getTipodocumentoid())
						.getNombre()
						+ " - # " + ingreso.getCodigodocumento());
				kardex.setFecha(timestamp());
				kardex.setCantidad(detalleIngreso.getCantidad());
				kardex.setPrecio(detalleIngreso.getPrecio());
				kardex.setActivo(false);
				kardexService.insertar(kardex);

				Map<String, Object> parametro = kardexService.ponderarPrecio(
						producto, bodega.getBodegaid(), kardex.getPrecio(),
						kardex.getCantidad());
				BigDecimal valorNuevoPrecio = (BigDecimal) parametro
						.get("precio");
				int cantidad = (Integer) parametro.get("cantidad");

				kardexService.insertar(kardexService.generarKardexSaldo(kardex,
						cantidad, valorNuevoPrecio, true));
			}
			presentaMensaje(FacesMessage.SEVERITY_INFO,
					"INGRESO A BODEGA EXITOSO");
		}
		return retorno;
	}

	public void insertarDetalle(Ingreso ingreso, Producto producto,
			Bodega bodega, Detalleingreso detalleIngreso) {
		if (producto == null) {
			presentaMensaje(FacesMessage.SEVERITY_ERROR, "ESCOJA UN PRODUCTO",
					"error", false);
		} else if (bodega == null || bodega.getBodegaid() == null
				|| bodega.getBodegaid() == 0) {
			presentaMensaje(FacesMessage.SEVERITY_ERROR, "ESCOJA UNA BODEGA",
					"error", false);
		} else if (detalleIngreso.getCantidad() == 0) {
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"INGRESE UNA CANTIDAD", "error", false);
		} else if (detalleIngreso.getPrecio().compareTo(newBigDecimal()) == 0) {
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"INGRESE EL PRECIO COSTO", "error", false);
		} else {
			if (ingreso.getDetalleingresos() == null)
				ingreso.setDetalleingresos(new ArrayList<Detalleingreso>());
			producto = productoService.obtenerPorProductoId(producto
					.getProductoid());
			detalleIngreso.setProducto(producto);
			boolean bn = true;
			for (Detalleingreso di : ingreso.getDetalleingresos())
				if (di.getProducto()
						.getProductoid()
						.compareTo(detalleIngreso.getProducto().getProductoid()) == 0) {
					bn = false;
					break;
				}
			if (bn) {
				detalleIngreso.setBodega(bodegaService
						.obtenerPorBodegaId(bodega.getBodegaid()));
				detalleIngreso.setDescuento(newBigDecimal());
				ingreso.addDetalleingreso(detalleIngreso);

				presentaMensaje(FacesMessage.SEVERITY_INFO,
						"INSERTÓ EL DETALLE: " + producto.getNombreimprimir(),
						"error", true);
			} else
				presentaMensaje(
						FacesMessage.SEVERITY_WARN,
						"EL PRODUCTO YA ESTA REGISTRADO MODIFIQUE SU CANTIDAD O PRECIO",
						"error", true);
		}
	}

	public List<Ingreso> obtener(Boolean activo) {
		List<Ingreso> list = ingresoDao.obtener(Ingreso.class, "ingresoid",
				activo);
		return list;
	}

	public List<Ingreso> obtener(String criterioBusquedaCliente,
			String criterioBusquedaCodigo, String criterioBusquedaDetalle) {
		criterioBusquedaCliente = criterioBusquedaCliente.toUpperCase();
		criterioBusquedaCodigo = criterioBusquedaCodigo.toUpperCase();
		criterioBusquedaDetalle = criterioBusquedaDetalle.toUpperCase();
		List<Ingreso> list = new ArrayList<Ingreso>();
		if (criterioBusquedaCliente.compareToIgnoreCase("") == 0
				&& criterioBusquedaCodigo.compareToIgnoreCase("") == 0
				&& criterioBusquedaDetalle.compareToIgnoreCase("") == 0)
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"INGRESE UN CRITERIO DE BUSQUEDA");
		else {
			if (criterioBusquedaCliente.length() >= 1
					&& criterioBusquedaCliente.length() <= 4)
				presentaMensaje(FacesMessage.SEVERITY_ERROR,
						"INGRESE MAS DE 4 CARACTERES PARA LA BÚSQUEDA POR CLIENTES");
			else if (!criterioBusquedaCodigo.matches("[0-9]*"))
				presentaMensaje(FacesMessage.SEVERITY_ERROR,
						"INGRESE SOLO NÚMEROS PARA LA BÚSQUEDA POR DOCUMENTOS");
			else if (criterioBusquedaCodigo.compareToIgnoreCase("") != 0
					&& criterioBusquedaCodigo.charAt(0) == '0')
				presentaMensaje(FacesMessage.SEVERITY_ERROR,
						"NO SE PERMITE CEROS AL INICIO PARA LA BÚSQUEDA POR DOCUMENTOS");
			else if (criterioBusquedaDetalle.length() >= 1
					&& criterioBusquedaDetalle.length() <= 3)
				presentaMensaje(FacesMessage.SEVERITY_ERROR,
						"INGRESE MAS DE 3 CARACTERES PARA LA BÚSQUEDA POR PRODUCTOS");
			else {
				if (criterioBusquedaCliente.compareTo("") != 0
						&& criterioBusquedaCodigo.compareTo("") != 0)
					list = ingresoDao
							.obtenerPorHql(
									"select distinct i from Ingreso i "
											+ "inner join fetch i.detalleingresos "
											+ "inner join i.proveedor pd inner join pd.persona p "
											+ "where ((p.cedula like ?1 or p.apellido like ?1 or p.nombre like ?1) "
											+ "and i.codigodocumento like ?2) "
											+ "order by i.fechafactura asc",
									new Object[] {
											"%" + criterioBusquedaCliente + "%",
											"%" + criterioBusquedaCodigo + "%" });
				else if (criterioBusquedaCliente.compareTo("") != 0)
					list = ingresoDao
							.obtenerPorHql(
									"select distinct i from Ingreso i "
											+ "inner join fetch i.detalleingresos "
											+ "inner join i.proveedor pd inner join pd.persona p "
											+ "where (p.cedula like ?1 or p.apellido like ?1 or p.nombre like ?1) "
											+ "order by i.fechafactura asc",
									new Object[] { "%"
											+ criterioBusquedaCliente + "%" });
				else if (criterioBusquedaCodigo.compareTo("") != 0)
					list = ingresoDao
							.obtenerPorHql(
									"select distinct i from Ingreso i "
											+ "inner join fetch i.detalleingresos "
											+ "inner join i.proveedor pd inner join pd.persona p "
											+ "where i.codigodocumento like ?1 "
											+ "order by i.fechafactura asc",
									new Object[] { "%" + criterioBusquedaCodigo
											+ "%" });
				else if (criterioBusquedaDetalle.compareTo("") != 0) {
					List<Ingreso> list1 = ingresoDao
							.obtenerPorHql(
									"select distinct i from Ingreso i "
											+ "inner join fetch i.detalleingresos di "
											+ "inner join fetch di.producto p "
											+ "where (p.nombre like ?1 or p.ean like ?1 or p.codigo1 like ?1 or p.codigo2 like ?1) "
											+ "order by i.fechafactura asc",
									new Object[] { "%"
											+ criterioBusquedaDetalle + "%" });
					for (Ingreso ingreso : list1) {
						ingresoDao.evict(ingreso);
						list.add(obtenerPorIngresoId(ingreso.getIngresoid()));
					}
				}
				if (list.isEmpty())
					presentaMensaje(FacesMessage.SEVERITY_INFO,
							"NO SE ENCONTRARON COINCIDENCIAS");
			}
		}
		return list;
	}

	@SuppressWarnings("unchecked")
	public List<ChequeReporte> obtenerChequesPorFechas(Date fechaInicio,
			Date fechaFin) {
		List<ChequeReporte> list = null;
		if (fechaInicio == null && fechaFin == null) {
			list = (List<ChequeReporte>) ingresoDao
					.obtenerPorSql1(
							"select ci.cuotaingresoid||ci.chequevaucher||ci.cuentatarjeta as id, "
									+ "ci.banco as banco, "
									+ "ci.chequevaucher as numeroCheque, "
									+ "ci.cuentatarjeta as numeroCuenta, "
									+ "ci.fechacheque as fechaEmision "
									+ "from invfac.cuotaingreso ci "
									+ "inner join invfac.tipopago tp on (tp.tipopagoid=ci.tipopagoid) "
									+ "where tp.nombre='CHEQUE' and ci.pagado=false "
									+ "order by ci.fechacheque;",
							ChequeReporte.class);
		} else if (fechaInicio == null) {
			list = (List<ChequeReporte>) ingresoDao
					.obtenerPorSql1(
							"select ci.cuotaingresoid||ci.chequevaucher||ci.cuentatarjeta as id, "
									+ "ci.banco as banco, "
									+ "ci.chequevaucher as numeroCheque, "
									+ "ci.cuentatarjeta as numeroCuenta, "
									+ "ci.fechacheque as fechaEmision "
									+ "from invfac.cuotaingreso ci "
									+ "inner join invfac.tipopago tp on (tp.tipopagoid=ci.tipopagoid) "
									+ "where tp.nombre='CHEQUE' and ci.pagado=false and ci.fechacheque<='"
									+ fechaFormatoString(fechaFin)
									+ "' order by ci.fechacheque;",
							ChequeReporte.class);
		} else if (fechaFin == null) {
			list = (List<ChequeReporte>) ingresoDao
					.obtenerPorSql1(
							"select ci.cuotaingresoid||ci.chequevaucher||ci.cuentatarjeta as id, "
									+ "ci.banco as banco, "
									+ "ci.chequevaucher as numeroCheque, "
									+ "ci.cuentatarjeta as numeroCuenta, "
									+ "ci.fechacheque as fechaEmision "
									+ "from invfac.cuotaingreso ci "
									+ "inner join invfac.tipopago tp on (tp.tipopagoid=ci.tipopagoid) "
									+ "where tp.nombre='CHEQUE' and ci.pagado=false and ci.fechacheque>='"
									+ fechaFormatoString(fechaInicio)
									+ "' order by ci.fechacheque;",
							ChequeReporte.class);
		} else {
			list = (List<ChequeReporte>) ingresoDao
					.obtenerPorSql1(
							"select ci.cuotaingresoid||ci.chequevaucher||ci.cuentatarjeta as id, "
									+ "ci.banco as banco, "
									+ "ci.chequevaucher as numeroCheque, "
									+ "ci.cuentatarjeta as numeroCuenta, "
									+ "ci.fechacheque as fechaEmision "
									+ "from invfac.cuotaingreso ci "
									+ "inner join invfac.tipopago tp on (tp.tipopagoid=ci.tipopagoid) "
									+ "where tp.nombre='CHEQUE' and ci.pagado=false  and ci.fechacheque >='"
									+ fechaFormatoString(fechaInicio)
									+ "' and ci.fechacheque<='"
									+ fechaFormatoString(fechaFin)
									+ "' order by ci.fechacheque;",
							ChequeReporte.class);
		}
		return list;
	}

	public Ingreso obtenerPorIngresoId(Long ingresoId) {
		Ingreso ingreso = (Ingreso) ingresoDao.obtenerPorHql(
				"select distinct i from Ingreso i "
						+ "left join fetch i.detalleingresos "
						+ "where i.ingresoid=?1", new Object[] { ingresoId })
				.get(0);

		return ingreso;
	}

}